﻿using Core.App.Attributes;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Quartz;
using Quartz.Impl;
using Quartz.Logging;
using Quartz.Spi;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace Core.QuartzHelper
{
    public static class QuartzServices
    {
        /// <summary>
        /// quartz服务注册方法
        /// </summary>
        /// <param name="services"></param>
        /// <param name="JobAndCron">需要执行的定时任务类名和定时cron表达式</param>
        public static void AddQuartz(this IServiceCollection services, Dictionary<Type,string> JobsAndCron)
        {
            //注册job工厂实现类
            services.AddSingleton<IJobFactory, QuartzFactory>();
            //注册日志实现类
            services.AddSingleton<ILogProvider, ConsoleLogProvider>();
            Dictionary<IJobDetail, IReadOnlyCollection<ITrigger>> dictionary = new Dictionary<IJobDetail, IReadOnlyCollection<ITrigger>>();
            object[] parameters = new object[1] { services };
            MethodInfo DependServicesMethod = typeof(ServiceCollectionServiceExtensions)
                        .GetMethods()
                        .Where(t => t.Name == "AddScoped" && t.IsStatic && t.GetParameters().Length == 1 && t.GetGenericArguments().Length == 1)
                        .FirstOrDefault();
            foreach (Type job in JobsAndCron.Keys)
            {
                if (!typeof(IJob).IsAssignableFrom(job))
                {
                    //如果任务类不继承IJob，就不注册这个定时任务,防止报错
                    continue;
                }
                else
                {
                    #region 反射调用依赖注入方法
                    /***
                     * 下面两行代码的效果等同于services.AddScoped<T>()
                     */
                    MethodInfo GenericMethod = DependServicesMethod.MakeGenericMethod(job);
                    GenericMethod.Invoke(null, parameters);
                    #endregion
                    IJobDetail Job = JobBuilder.Create(job)
                        .WithIdentity($"Job:{job.Name}")
                        .Build();
                    ITrigger trigger = TriggerBuilder.Create()
                        .WithIdentity($"trigger:{job.Name}")
                        .StartNow()
                        .WithSchedule(CronScheduleBuilder.CronSchedule(JobsAndCron[job]))
                        .ForJob(Job)
                        .Build();
                    dictionary.Add(Job, new HashSet<ITrigger> { trigger });
                }
               
            }
            //所有触发器和依赖注入项全部准备完毕之后再建立StdSchedulerFactory
            StdSchedulerFactory schedulerFactory = new StdSchedulerFactory();
            IScheduler scheduler = schedulerFactory.GetScheduler().GetAwaiter().GetResult();
            ServiceProvider provider = services.BuildServiceProvider();
            //使用自己实现的jobfactory接管原工厂,这样job就能使用依赖注入了
            scheduler.JobFactory = provider.GetService<IJobFactory>();
            //接管日志(没有日志框架的情况下才通过这个方法输出日志)
            LogProvider.SetCurrentLogProvider(provider.GetService<ILogProvider>());
            scheduler.ScheduleJobs(dictionary, true);
            scheduler.Start();
        }

        
    }
}
